package utils;

import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class ResourceUtils {

    private ResourceUtils() {
    }

    public static InputStream parseConfig(Class<?> cls, String fileName) throws IOException {
        URL url = cls.getProtectionDomain().getClassLoader().getResource(fileName);
        assert url != null;
        return url.openStream();
    }

    public static InputStream parseConfig(Object obj, String fileName) throws IOException {
//        String path = obj.getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
//        if (path.endsWith(".jar")) {
//            if (!fileName.startsWith("/")) {
//                fileName = "/" + fileName;
//            }
//            path = String.format("jar:file:%s!%s", path, fileName);
//        } else {
//            if (fileName.startsWith("/")) {
//                fileName = fileName.substring(1);
//            }
//            path = String.format("file:%s%s", path, fileName);
//        }
//        path = java.net.URLDecoder.decode(path, StandardCharsets.UTF_8);
//        URL url = new URL(path);
        URL url = obj.getClass().getProtectionDomain().getClassLoader().getResource(fileName);
        assert url != null;
        return url.openStream();
    }

    public static List<InputStream> parseConfigs(File rootFile, boolean recursion,
                                                 String expression) throws IOException {
        List<InputStream> result = new ArrayList<>(10);
        parseRecursively(rootFile, recursion, parseExpression(expression), result);
        return result;
    }

    private static String parseExpression(String str) {
        String[] values = str.split("\\*");
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < values.length; i++) {
            builder.append("(").append(values[i]).append(")");
            if (i < values.length - 1) {
                builder.append(".+");
            }
        }
        return builder.toString();
    }

    private static void parseRecursively(File rootFile, boolean recursion,
                                         String expression, List<InputStream> fileStreams) throws FileNotFoundException {
        File[] files = rootFile.listFiles();
        if (Objects.isNull(files) || files.length == 0) {
            return;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                if (!recursion) {
                    continue;
                }
                parseRecursively(file, true, expression, fileStreams);
            }
            if (file.getName().matches(expression)) {
                fileStreams.add(new FileInputStream(file));
            }
        }
    }


    public static List<InputStream> parseConfigs(Object obj, String pathName) throws IOException {
        List<String> files = new ArrayList<>(10);
        parsePath(obj, files, pathName);
        List<InputStream> inputStreams = new ArrayList<>(files.size());
        for (String file : files) {
            URL url = new URL(file);
            inputStreams.add(url.openStream());
        }
        return inputStreams;
    }

    private static void parsePath(Object obj, List<String> files, String pathName) throws IOException {
//        String realPath = obj.getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
        String root = pathName.substring(0, pathName.indexOf("**"));
        String realPath = Objects.requireNonNull(obj.getClass().getProtectionDomain().getClassLoader().getResource(root)).getPath();
        StringBuilder rootBuilder = new StringBuilder();
        String fileContent = pathName.substring(pathName.lastIndexOf("/") + 1);
        fileContent = convertExpression(fileContent);
        boolean isJar = realPath.endsWith(".jar");
        realPath = realPath.startsWith("/") ? realPath.substring(1) : realPath;
        if (isJar) {
            rootBuilder.append(realPath).append("!");
        } else {
            rootBuilder.append(realPath);
        }
        Path rootPath = Paths.get(rootBuilder.toString());
        readFile(rootPath, files, fileContent, isJar);
    }

    private static void readFile(Path rootPath, List<String> files, String fileContent, boolean isJar) throws IOException {
        try (var paths = Files.newDirectoryStream(rootPath)) {
            paths.forEach(path -> {
                File t = path.toFile();
                if (t.isDirectory()) {
                    try {
                        readFile(path, files, fileContent, isJar);
                        return;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (!t.getName().matches(fileContent)) {
                    return;
                }
                String tmp = t.getAbsolutePath();
                if (isJar) {
                    tmp = "jar:file:\\" + tmp + "!";
                } else {
                    tmp = "file:\\" + tmp;
                }
                files.add(URLDecoder.decode(tmp, StandardCharsets.UTF_8));
            });
        }
    }

    private static String convertExpression(String str) {
        StringBuilder builder = new StringBuilder();
        if (str.equals("\\*")) {
            builder.append(".+");
        } else if (str.startsWith("*")) {
            builder.append(str.replaceAll("\\*", ".+(")).append(")");
        } else if (!str.startsWith("*") && !str.endsWith("*")) {
            builder.append("(").append(str.replaceAll("\\*", ").+(")).append(")");
        }
        return builder.toString();
    }

}
